package com.cjf.aop.runtime.aspectj;

import android.os.Looper;

import com.cjf.aop.annotation.WorkThread;
import com.cjf.aop.enums.ThreadType;
import com.cjf.aop.runtime.logger.XLogger;
import com.cjf.aop.runtime.utils.AppExecutors;
import com.cjf.aop.runtime.utils.Utils;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

import java.util.concurrent.Callable;

/**
 * 线程切片
 * created by cjf
 * on: 2020/3/25
 */
@Aspect
public class ThreadAspectJ {

    /**
     * 带 {@link com.cjf.aop.annotation.WorkThread} 注解的所有类
     */
    @Pointcut("within(@com.cjf.aop.annotation.WorkThread *)")
    public void withinAnnotatedClass() {
    }

    //带有WorkThread注解的所有类,除去synthetic修饰的方法
    @Pointcut("execution(!synthetic * *(..)) && withinAnnotatedClass()")
    public void methodInsideAnnotatedType() {
    }

    //方法切入点，带有@Thread注解的方法,或者带有@WorkThread注解的类并除去synthetic修饰的方法
    @Pointcut("execution(@com.cjf.aop.annotation.WorkThread * *(..)) || methodInsideAnnotatedType()")
    public void method() {
    }

    @Around("method() && @annotation(workThread)")
    public Object aroundJoinPoint(final ProceedingJoinPoint joinPoint, WorkThread workThread) throws Throwable {
        boolean isMainThread = workThread.value() == ThreadType.MAIN;
        //当前线程为主线程，并希望方法运行在主线程；当前线程为子线程，并希望方法运行在子线程
        if ((Looper.getMainLooper() == Looper.myLooper() && isMainThread) ||
                (Looper.getMainLooper() != Looper.myLooper() && !isMainThread)) {
            return joinPoint.proceed();
        }
        XLogger.d(Utils.getMethodDescribeMsg(joinPoint) + " \u21E2 [当前线程]:" +
                java.lang.Thread.currentThread().getName() + "，正在切换到" + (workThread.value() == ThreadType.MAIN ? "主线程" : "子线程"));
        Object result = null;
        switch (workThread.value()) {
            case ThreadType.IO_Single:
            case ThreadType.IO_Disk:
                result = AppExecutors.get().singleIO().submit(new Callable<Object>() {
                    @Override
                    public Object call() throws Exception {
                        //任务在该线程上执行，并返回执行结果
                        return getProcessResult(joinPoint);
                    }
                }).get();
                break;
            case ThreadType.IO_Fixed:
            case ThreadType.IO_Network:
                result = AppExecutors.get().poolIO().submit(new Callable<Object>() {
                    @Override
                    public Object call() throws Exception {
                        return getProcessResult(joinPoint);
                    }
                }).get();
                break;
            case ThreadType.MAIN:
                result = AppExecutors.get().mainThread().submit(new Callable<Object>() {
                    @Override
                    public Object call() throws Exception {
                        //Thread.sleep(1000);
                        return getProcessResult(joinPoint);
                    }
                }).get();
                break;
        }
        XLogger.d(Utils.getMethodDescribeMsg(joinPoint) + " \u21E0 [执行结果]:" + Utils.toString(result));
        return result;
    }

    /**
     * 获取执行结果
     *
     * @param joinPoint
     */
    private Object getProcessResult(ProceedingJoinPoint joinPoint) {
        try {
            return joinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
            XLogger.e(e);
        }
        return null;
    }
}
